package core.util;

import java.util.HashMap;
import java.util.Map;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;

public class JsonTunnelDataImpl implements TunnelData {

	private JSONObject jsonObject;

	private Map<String, Object> cache = new HashMap<String, Object>();

	private static NullObject nullValue = new NullObject();
	
	private static class NullObject {
	}

	public JsonTunnelDataImpl(String tunnelData) {
		if (tunnelData == null) {
			jsonObject = new JSONObject();
			return;
		}

		jsonObject = JSONObject.fromObject(tunnelData);
	}

	private void storeValueToCache(String tagName, Object value) {
		if ( value == null ) {
			cache.put(tagName, nullValue);
		} else {
			cache.put(tagName, value);
		}
	}
	
	public Object getData(String tagName) {
		Object value = cache.get(tagName);
		if (value != null) {
			if (value == nullValue) {
				return null;
			} else {
				return value;
			}
		}

		value = jsonObject.opt(tagName);
		storeValueToCache(tagName, value);
		return value;
	}

	public String getTunnelData() {
		return jsonObject.toString();
	}

	private Object getDataImpl(String tagName, Class<?> beanClass) {
		Object obj = jsonObject.opt(tagName);
		if (obj == null) {
			return null;
		}

		if (obj instanceof JSONArray) {
			JSONArray jsa = (JSONArray) obj;
			JsonConfig jcfg = new JsonConfig();
			jcfg.setHandleJettisonEmptyElement(true);
			jcfg.setRootClass(beanClass);
			return JSONArray.toList(jsa, jcfg);
		} else if (obj instanceof JSONObject) {
			return JSONObject.toBean((JSONObject) obj, beanClass);
		}

		return obj;
	}
	
	public Object getData(String tagName, Class<?> beanClass) {
		Object value = cache.get(tagName);
		if (value != null) {
			if (value == nullValue) {
				return null;
			} else {
				return value;
			}
		}

		value = getDataImpl(tagName, beanClass);
		storeValueToCache(tagName, value);
		
		return value;	
	}

	public Object removeData(String tagName) {
		Object value = jsonObject.opt(tagName);
		jsonObject.remove(tagName);
		storeValueToCache(tagName, null);
		return value;
	}

	public Object setData(String tagName, Object value) {
		Object oldVal = jsonObject.opt(tagName);
		jsonObject.element(tagName, value);
		storeValueToCache(tagName, value);
		return oldVal;
	}

	@SuppressWarnings("unchecked")
	public Map<String, Object> getDatas() {
		return (Map<String, Object>) jsonObject;
	}

}
